package study.https.auth.https;

import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.CollectionUtils;

import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import java.io.File;
import java.io.FileInputStream;
import java.security.KeyStore;
import java.util.*;

public class HttpsUtil {

    private static PoolingHttpClientConnectionManager secureConnectionManager;
    private static HttpClientBuilder secureHttpBulder = null;
    private static RequestConfig requestConfig = null;
    private static int MAXCONNECTION = 100;
    private static int DEFAULTMAXCONNECTION = 5;

    private static String CLIENT_KEY_STORE ;
    private static String CLIENT_KEY_STORE_PASSWORD ;

    @Value("${server.ssl.key-store}")
    public void setClientKeyStore(String clientKeyStore) {
        CLIENT_KEY_STORE = clientKeyStore;
    }
    @Value("${server.ssl.key-store-password}")
    public static void setClientKeyStorePassword(String clientKeyStorePassword) {
        CLIENT_KEY_STORE_PASSWORD = clientKeyStorePassword;
    }

    static {
        //设置http的状态参数
        requestConfig = RequestConfig.custom()
                .setSocketTimeout(10000)
                .setConnectTimeout(10000)
                .setConnectionRequestTimeout(10000)
                .build();

        try {

            KeyManagerFactory keyFactory = KeyManagerFactory.getInstance("SunX509", "SunJSSE");
            KeyStore clientKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
            FileInputStream clientKeyStoreInput = new FileInputStream(new File(CLIENT_KEY_STORE));
            clientKeyStore.load(clientKeyStoreInput, CLIENT_KEY_STORE_PASSWORD.toCharArray());
            keyFactory.init(clientKeyStore, CLIENT_KEY_STORE_PASSWORD.toCharArray());// 加载私钥
            KeyManager[] keyManagers = keyFactory.getKeyManagers();
            // 加载信任证书
            MyTrustManager myTrustManager = new MyTrustManager();
            // SSL , SSLV2  已经基本弃用，连接服务端时会握手失败
            SSLContext sslContext = SSLContext.getInstance(SSLConnectionSocketFactory.TLS);
            sslContext.init(keyManagers, new TrustManager[]{myTrustManager}, null);
            Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                    .register("http", PlainConnectionSocketFactory.INSTANCE)
                    // 正常的SSL连接会验证码所有证书信息
                    // .register("https", new SSLConnectionSocketFactory(sslcontext)).build();
                    //  只忽略域名验证
                    .register("https", new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE)).build();

            secureConnectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
            secureConnectionManager.setMaxTotal(MAXCONNECTION);
            //设置每个Route的连接最大数
            secureConnectionManager.setDefaultMaxPerRoute(DEFAULTMAXCONNECTION);
            //设置指定域的连接默认最大数
            secureConnectionManager.setDefaultMaxPerRoute(20);
            secureHttpBulder = HttpClients.custom().setConnectionManager(secureConnectionManager);
        } catch (Exception e) {
            throw new Error("Failed to initialize the server-side SSLContext", e);
        }
    }

    private static CloseableHttpClient getSecureConnection() throws Exception {
        return secureHttpBulder.build();
    }


    private static HttpUriRequest getRequestMethod(Map<String, String> headers, Map<String, String> paramsMap, String url, String method) {
        List<NameValuePair> params = new ArrayList<NameValuePair>();
        if (!CollectionUtils.isEmpty(paramsMap)) {
            Set<Map.Entry<String, String>> paramsEntrySet = paramsMap.entrySet();
            for (Map.Entry<String, String> e : paramsEntrySet) {
                String name = e.getKey();
                String value = e.getValue();
                NameValuePair pair = new BasicNameValuePair(name, value);
                params.add(pair);
            }
        }

        HttpUriRequest reqMethod = null;
        if ("post".equals(method)) {
            reqMethod = RequestBuilder.post().setUri(url)
                    .addParameters(params.toArray(new BasicNameValuePair[params.size()]))
                    .setConfig(requestConfig).build();
        } else if ("get".equals(method)) {
            reqMethod = RequestBuilder.get().setUri(url)
                    .addParameters(params.toArray(new BasicNameValuePair[params.size()]))
                    .setConfig(requestConfig).build();
        }
        if (!CollectionUtils.isEmpty(headers)) {
            Set<Map.Entry<String, String>> headersEntrySet = headers.entrySet();
            for (Map.Entry<String, String> header : headersEntrySet) {
                reqMethod.setHeader(header.getKey(), header.getValue());
            }
        }
        return reqMethod;
    }

    /**
     * tls 双向认证的 get 请求
     * @param headerMap  请求头数据
     * @param url
     * @return
     */
    public static HttpResponse get(Map<String, String> headerMap, String url) {
        HttpResponse response = null;
        try {
            HttpClient client = getSecureConnection();
            HttpUriRequest post = getRequestMethod(headerMap, null, url, "get");
            response = client.execute(post);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return response;
    }

    /**
     * tls 双向认证的 post 请求
     * @param headerMap 请求头数据
     * @param paramMap 请求体数据
     * @param url
     * @return
     */
    public static HttpResponse post(Map<String, String> headerMap, Map<String, String> paramMap, String url) {
        HttpResponse response = null;
        try {
            HttpClient client = getSecureConnection();
            HttpUriRequest post = getRequestMethod(headerMap, paramMap, url, "post");
            response = client.execute(post);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return response;
    }


}
